/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.support.sql;

import cn.easyplatform.FieldNotFoundException;
import cn.easyplatform.contexts.RecordContext;
import cn.easyplatform.dos.FieldDo;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.support.scripting.RhinoScriptable;
import cn.easyplatform.support.word.ReserveWordFactory;
import cn.easyplatform.util.RuntimeUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public abstract class AbstractSqlParser<T> implements SqlParser {

    private List<T> params = new ArrayList<>();

    public String parseQuery(String sql, RecordContext target) {
        sql = sql.trim();
        if (sql.startsWith("$")) {
            sql = (String) target.getValue(sql.substring(1));
            if (sql != null)
                sql = sql.trim();
            if (Strings.isBlank(sql))
                return "";
        }
        StringBuilder s = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        char[] cs = sql.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            if (cs[i] == '#') {// 直接替换为栏位值
                i++;
                sb.setLength(0);
                Character c = null;
                if (cs[i] == '{') {// #{xxx}
                    i++;
                    do {
                        sb.append(cs[i++]);
                    } while (cs[i] != '}');
                } else {// #xxx
                    c = ' ';
                    while (i < cs.length) {
                        if (!Character.isLetterOrDigit(cs[i]) && cs[i] != '_') {
                            c = cs[i];
                            break;
                        } else
                            sb.append(cs[i]);
                        i++;
                    }
                }
                String name = sb.toString();
                Object value = target.getValue(name);
                s.append(value);
                if (c != null)
                    s.append(c);
            } else
                s.append(cs[i]);
        }
        sb = null;
        return s.toString();
    }

    public String parse(String sql, RhinoScriptable scope, RecordContext... rcs) {
        RecordContext target = null, source = null;
        if (rcs.length > 1) {
            target = rcs[1];
            source = rcs[0];
        } else if (rcs.length == 1)
            target = rcs[0];
        sql = sql.trim();
        if (sql.startsWith("$")) {
            sql = (String) target.getValue(sql.substring(1));
            if (sql != null)
                sql = sql.trim();
            if (Strings.isBlank(sql))
                return "";
        }

        StringBuilder s = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        // 先处理#
        char[] cs = sql.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            if (cs[i] == '#') {// 直接替换为栏位值
                i++;
                sb.setLength(0);
                Character c = null;
                if (cs[i] == '{') {// #{xxx}
                    i++;
                    do {
                        sb.append(cs[i++]);
                    } while (cs[i] != '}');
                } else {// #xxx
                    c = ' ';
                    while (i < cs.length) {
                        if (!Character.isLetterOrDigit(cs[i]) && cs[i] != '_') {
                            c = cs[i];
                            break;
                        } else
                            sb.append(cs[i]);
                        i++;
                    }
                }
                String name = sb.toString();
                Object value = null;
                if (scope == null)
                    value = target.getValue(name);
                else {
                    FieldDo fd = target.getFieldQuietly(name);
                    if (fd == null) {
                        value = scope.getVariable(name);
                        if (value == null)
                            throw new FieldNotFoundException(
                                    "entity.table.variable.not.found", name);
                        value = RuntimeUtils.castTo(value);
                    } else
                        value = fd.getValue();
                }
                s.append(value);
                if (c != null)
                    s.append(c);
            } else
                s.append(cs[i]);
        }
        sql = s.toString();
        s.setLength(0);
        cs = sql.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            if (cs[i] == '$') {// 参数，转为xx=?
                i++;
                sb.setLength(0);
                char c = ' ';
                while (i < cs.length) {
                    if (!Character.isLetterOrDigit(cs[i]) && cs[i] != '_') {
                        c = cs[i];
                        break;
                    } else
                        sb.append(cs[i]);
                    i++;
                }
                FieldDo fd = null;
                String name = sb.toString();
                if (scope == null)
                    fd = target.getField(name);
                else {
                    fd = target.getFieldQuietly(name);
                    if (fd == null) {
                        if (scope.containsKey(name)) {
                            Object value = scope.getVariable(name);
                            if (value != null) {
                                value = RuntimeUtils.castTo(value);
                                fd = RuntimeUtils.castTo(name, value);
                            } else
                                fd = FieldDo.NULL();
                        } else
                            throw new FieldNotFoundException(
                                    "entity.table.variable.not.found", name);
                    }
                }
                if (fd.getValue() != null && fd.getValue() instanceof Object[]) {
                    Object[] values = (Object[]) fd.getValue();
                    for (int j = 0; j < values.length; j++) {
                        s.append("?");
                        if (j < values.length - 1)
                            s.append(",");
                        params.add(getVariable(fd.getName(), values[j]));
                    }
                }else if (fd.getValue() instanceof Collection) {
                    Iterator<Object> itr = ((Collection) fd.getValue()).iterator();
                    while (itr.hasNext()) {
                        s.append("?");
                        params.add(getVariable(fd.getName(), itr.next()));
                        if (itr.hasNext())
                            s.append(",");
                    }
                } else {
                    s.append("?");
                    params.add(getVariable(fd.getName(), fd));
                }
                s.append(c);
            } else if (cs[i] == '&' && (i + 1) < cs.length && Character.isLetter(cs[i + 1])) {// 保留字
                i++;
                sb.setLength(0);
                while (i < cs.length) {
                    if (!Character.isLetter(cs[i])) {
                        i--;
                        break;
                    }
                    sb.append(cs[i]);
                    i++;
                }
                s.append("?");
                String name = sb.toString();
                params.add(getVariable(name, ReserveWordFactory.getReserveWordAs(name)));
            } else if (cs[i] == '#') {// 直接替换为栏位值
                i++;
                sb.setLength(0);
                Character c = null;
                if (cs[i] == '{') {// #{xxx}
                    i++;
                    do {
                        sb.append(cs[i++]);
                    } while (cs[i] != '}');
                } else {// #xxx
                    c = ' ';
                    while (i < cs.length) {
                        if (!Character.isLetterOrDigit(cs[i]) && cs[i] != '_') {
                            c = cs[i];
                            break;
                        } else
                            sb.append(cs[i]);
                        i++;
                    }
                }
                String name = sb.toString();
                Object value = null;
                if (scope == null)
                    value = target.getValue(name);
                else {
                    FieldDo fd = target.getFieldQuietly(name);
                    if (fd == null) {
                        value = scope.getVariable(name);
                        if (value == null)
                            throw new FieldNotFoundException(
                                    "entity.table.variable.not.found", name);
                        value = RuntimeUtils.castTo(value);
                    } else
                        value = fd.getValue();
                }
                s.append(value);
                if (c != null)
                    s.append(c);
            } else
                s.append(cs[i]);
        }
        sb = null;
        return s.toString();
    }

    @Override
    public String parse(String sql, RecordContext... rcs) {
        return parse(sql, null, rcs);
    }

    protected abstract T getVariable(String name, Object value);

    public List<T> getParams() {
        return params;
    }
}
